Skip to content

3648: AVRO-4229 - Update StatsServer resource handler#34

Open
martin-augment wants to merge 1 commit intomainfrom
pr-3648-2026-02-13-06-28-37
Open

3648: AVRO-4229 - Update StatsServer resource handler#34
martin-augment wants to merge 1 commit intomainfrom
pr-3648-2026-02-13-06-28-37

Conversation

@martin-augment
Copy link
Copy Markdown
Owner

3648: To review by AI

@github-actions github-actions bot added the Java label Feb 13, 2026
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @martin-augment, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request updates the StatsServer's resource handling mechanism within Apache Avro's IPC-Jetty module. It modernizes the way static files are served by replacing a custom servlet with Jetty's built-in ResourceHandler and ServletContextHandler, which simplifies the server's configuration and aligns it with standard Jetty practices for serving both dynamic and static content.

Highlights

  • Static Resource Handling: The custom StaticServlet has been removed and replaced with Jetty's standard ResourceHandler for serving static files, improving adherence to modern Jetty practices.
  • Jetty Server Configuration: The StatsServer setup has been refactored to utilize ServletContextHandler for the main servlet and HandlerList to combine it with a ContextHandler for static resources, streamlining the server's handler management.
Changelog
  • lang/java/ipc-jetty/src/main/java/org/apache/avro/ipc/jetty/StaticServlet.java
    • Removed the custom StaticServlet class, which was previously responsible for serving static files.
  • lang/java/ipc-jetty/src/main/java/org/apache/avro/ipc/jetty/StatsServer.java
    • Updated imports to include necessary Jetty classes like ContextHandler, HandlerList, ResourceHandler, ServletContextHandler, and Resource.
    • Replaced the single ServletHandler with a HandlerList to manage multiple contexts.
    • Configured a ServletContextHandler for the StatsServlet to handle dynamic content at the root path.
    • Introduced a ResourceHandler to serve static files from the classpath resource /org/apache/avro/ipc/stats/static.
    • Wrapped the ResourceHandler in a ContextHandler to serve static content under the /static URL path.
Activity
  • The pull request has been marked for review by AI, indicating no human review activity has occurred yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@claude
Copy link
Copy Markdown

claude bot commented Feb 13, 2026

Code Review - PR #34: AVRO-4229 - Update StatsServer resource handler

Summary

This PR modernizes the static resource handling in StatsServer by replacing the custom StaticServlet with Jetty's built-in ResourceHandler. The change improves the code by using standard Jetty APIs instead of a custom servlet implementation.


✅ Positive Aspects

  1. Better separation of concerns: Using HandlerList with separate contexts for static resources and servlets is cleaner than the previous approach
  2. Uses standard Jetty APIs: Replaces custom servlet with built-in ResourceHandler, reducing maintenance burden
  3. Security improvement: Explicitly disables directory listing with setDirectoriesListed(false)
  4. Code reduction: Removes 45 lines of custom code in favor of standard framework functionality

🐛 Critical Issues

1. Incorrect resource path (StatsServer.java:56)

resourceHandler.setBaseResource(Resource.newClassPathResource("/org/apache/avro/ipc/stats/static"));

Problem: The static resources are located in the avro-ipc module at /org/apache/avro/ipc/stats/static, but StatsServer is in the avro-ipc-jetty module. This will cause a NullPointerException at runtime when the resources cannot be found.

Evidence: The static files are in /home/runner/work/avro/avro/lang/java/ipc/src/main/velocity/org/apache/avro/ipc/stats/static/ (the avro-ipc module), not in the avro-ipc-jetty module.

Fix: You need to ensure the avro-ipc module's resources are on the classpath when running StatsServer. Verify that:

  • The path is correct (it appears to be)
  • The avro-ipc dependency includes these resources in its JAR
  • Test that Resource.newClassPathResource("/org/apache/avro/ipc/stats/static") returns non-null

2. Changed URL structure breaks existing clients (StatsServer.java:60)

staticContext.setContextPath("/static");

Problem: Previously, static files were served from the root path (/). Now they're served from /static/*. This is a breaking change for any HTML templates or JavaScript that reference these resources.

Impact: The Velocity templates in StatsServlet likely reference static resources like:

  • avro.css
  • avro.js
  • jquery-1.6.3.min.js
  • protovis-r3.2.js
  • etc.

These references will now break unless they're updated to use the /static/ prefix.

Recommendation: Either:

  • Update all references in Velocity templates to use /static/ prefix
  • Or change staticContext.setContextPath("/") and handle routing differently

3. License header placement (StatsServer.java:1-21)

Problem: The license header is placed AFTER the import statements instead of at the beginning of the file.

Current:

package org.apache.avro.ipc.jetty;

import org.apache.avro.ipc.stats.StatsPlugin;
import org.apache.avro.ipc.stats.StatsServlet;
/*
 * Licensed to the Apache Software Foundation (ASF)...

Should be:

/*
 * Licensed to the Apache Software Foundation (ASF)...
 */
package org.apache.avro.ipc.jetty;

import org.apache.avro.ipc.stats.StatsPlugin;

⚠️ Potential Issues

4. Handler ordering matters (StatsServer.java:63-64)

The current handler order has staticContext before servletContext. This means:

  • /static/* → static resources
  • /* → StatsServlet

This is correct, but if both were set to /, there would be ambiguity. Since you've separated them with different context paths, this is fine, but the comment about the path change issue (#2) still applies.

5. No null check for resource (StatsServer.java:56)

If the classpath resource is not found, Resource.newClassPathResource() may return null, which could cause a NullPointerException when the server starts or serves requests. Consider adding:

Resource baseResource = Resource.newClassPathResource("/org/apache/avro/ipc/stats/static");
if (baseResource == null) {
    throw new IllegalStateException("Static resources not found on classpath");
}
resourceHandler.setBaseResource(baseResource);

📋 Test Coverage

Missing: There are no test files found for StatsServer.

Recommendation: Add integration tests that:

  1. Start a StatsServer instance
  2. Verify static resources are accessible at the expected URLs
  3. Verify the servlet endpoints still work
  4. Test the resource path resolution works correctly

🔒 Security Considerations

Good: Setting setDirectoriesListed(false) prevents directory traversal attacks.

Note: The old StaticServlet had path traversal protection by taking only the filename:

String[] parts = pathInContext.split("/");
String filename = parts[parts.length - 1];

Jetty's ResourceHandler should handle this securely by default, but verify that requests like /static/../../../etc/passwd are properly blocked.


📝 Recommendations

  1. Fix the license header placement immediately - this is a clear violation of Apache project standards
  2. Verify resource loading works - add null check and/or error handling
  3. Update Velocity templates - ensure all static resource references use the new /static/ path
  4. Add integration tests - verify the changes work end-to-end
  5. Document the breaking change - if the URL structure change is intentional, document it in the commit message or release notes

🎯 Verdict

Needs Changes - The approach is sound, but there are critical issues that must be addressed:

  • License header placement
  • Potential runtime NullPointerException
  • Breaking change in static resource URLs

The refactoring from custom servlet to standard Jetty handlers is a good modernization, but please address the issues above before merging.


🤖 Review generated by Claude Code

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the StatsServer to use a more modern Jetty handler-based approach for serving the StatsServlet and static resources. The custom StaticServlet is removed and replaced with Jetty's standard ResourceHandler, which improves code clarity, maintainability, and security. The use of HandlerList to combine different handlers is a good practice. I have one minor suggestion to make the code even more concise.

Comment on lines +63 to +64
HandlerList handlers = new HandlerList();
handlers.setHandlers(new org.eclipse.jetty.server.Handler[] { staticContext, servletContext });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

You can initialize the HandlerList and set the handlers in a single step using its constructor, which accepts a varargs Handler... argument. This makes the code more concise.

Suggested change
HandlerList handlers = new HandlerList();
handlers.setHandlers(new org.eclipse.jetty.server.Handler[] { staticContext, servletContext });
HandlerList handlers = new HandlerList(staticContext, servletContext);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value:good-to-have; category:bug; feedback: The Gemini AI reviewer is correct! The code could be simplified by using the overloaded constructor that accepts the list of contexts.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 13, 2026

Walkthrough

StaticServlet.java has been removed from the codebase. The StatsServer.java configuration was refactored to replace the previous ServletHandler approach with a HandlerList-based setup. The new configuration uses ServletContextHandler at the root path to host StatsServlet, and introduces a ResourceHandler backed by classpath resources to serve static files at the "/static" path. A ContextHandler wraps the static resource handler, and both contexts are combined using HandlerList as the HTTP server's handler.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch pr-3648-2026-02-13-06-28-37

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
lang/java/ipc-jetty/src/main/java/org/apache/avro/ipc/jetty/StatsServer.java (3)

1-5: License header is placed after package and imports.

The ASF license header (lines 5–21) appears after the package declaration and two import statements. Convention (and most ASF projects) requires the license block to be the very first thing in the file. This appears to be a pre-existing issue, but since you're modifying this file, it's a good opportunity to fix it.

♻️ Move the license header to the top of the file
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package org.apache.avro.ipc.jetty;
 
 import org.apache.avro.ipc.stats.StatsPlugin;
 import org.apache.avro.ipc.stats.StatsServlet;
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- ...
- */
 import org.eclipse.jetty.server.Server;

63-66: Handler ordering and FQN usage look correct, minor cleanup possible.

The ordering { staticContext, servletContext } is correct for HandlerList — static resources at /static are tried first, then the servlet context handles the rest. Since Handler is already importable from the existing Jetty server package, you could avoid the fully-qualified name on line 64.

♻️ Import Handler to avoid FQN

Add to imports:

import org.eclipse.jetty.server.Handler;

Then simplify line 64:

-    handlers.setHandlers(new org.eclipse.jetty.server.Handler[] { staticContext, servletContext });
+    handlers.setHandlers(new Handler[] { staticContext, servletContext });

45-68: No resource cleanup on startup failure.

If httpServer.start() on line 68 throws, the Server is left in an undefined state with the port potentially bound. Consider wrapping the start in a try-catch that calls stop() on failure, or documenting that the caller is responsible for cleanup.

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@augmentcode
Copy link
Copy Markdown

augmentcode bot commented Feb 13, 2026

🤖 Augment PR Summary

Summary: Updates the Jetty-based stats UI server to use Jetty handlers instead of a custom static-file servlet.

Changes:

  • Removes the custom StaticServlet implementation.
  • Refactors StatsServer to mount the stats page via a ServletContextHandler.
  • Adds a dedicated ResourceHandler under /static to serve UI assets from the classpath.
  • Uses a HandlerList to route between the static content context and the servlet context.

Technical Notes: Static asset serving is now based on a classpath resource base (org/apache/avro/ipc/stats/static) with directory listing disabled.

🤖 Was this summary useful? React with 👍 or 👎

Copy link
Copy Markdown

@augmentcode augmentcode bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 2 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

ServletHolder servletHolder = new ServletHolder(new StatsServlet(plugin));
servletContext.addServlet(servletHolder, "/");

ResourceHandler resourceHandler = new ResourceHandler();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This effectively replaces (and deletes) the public StaticServlet; if org.apache.avro.ipc.jetty is considered a supported API surface, this is a binary-compatibility break worth double-checking.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value:incorrect-but-reasonable; category:bug; feedback: The Augment AI reviewer is not correct! The "ipc-jetty" Maven module is an application, not a library, so there is no need to maintain API stability for it.

servletContext.addServlet(servletHolder, "/");

ResourceHandler resourceHandler = new ResourceHandler();
resourceHandler.setBaseResource(Resource.newClassPathResource("/org/apache/avro/ipc/stats/static"));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resource.newClassPathResource(...) can return null if the classpath base isn’t resolvable as a directory resource; if that happens the static UI assets may fail to serve (or cause runtime errors) without a clear message.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value:good-to-have; category:bug; feedback: The Augment AI reviewer is correct! Since the Java package is provided by another Maven module (the "ipc" module) it would be good to add a check that the result of Resource.newClassPathResource() is non-null and fail early at application start time if it is null for some reason. Prevents runtime exceptions at request time if the Java package is not found

@martin-augment
Copy link
Copy Markdown
Owner Author

5. No null check for resource (StatsServer.java:56)

If the classpath resource is not found, Resource.newClassPathResource() may return null, which could cause a NullPointerException when the server starts or serves requests. Consider adding:

Resource baseResource = Resource.newClassPathResource("/org/apache/avro/ipc/stats/static");
if (baseResource == null) {
    throw new IllegalStateException("Static resources not found on classpath");
}
resourceHandler.setBaseResource(baseResource);

value:good-to-have; category:bug; feedback: The Claude AI reviewer is correct! Since the Java package is provided by another Maven module (the "ipc" module) it would be good to add a check that the result of Resource.newClassPathResource() is non-null and fail early at application start time if it is null for some reason. Prevents runtime exceptions at request time if the Java package is not found

@martin-augment
Copy link
Copy Markdown
Owner Author

3. License header placement (StatsServer.java:1-21)

Problem: The license header is placed AFTER the import statements instead of at the beginning of the file.

Current:

package org.apache.avro.ipc.jetty;

import org.apache.avro.ipc.stats.StatsPlugin;
import org.apache.avro.ipc.stats.StatsServlet;
/*
 * Licensed to the Apache Software Foundation (ASF)...

Should be:

/*
 * Licensed to the Apache Software Foundation (ASF)...
 */
package org.apache.avro.ipc.jetty;

import org.apache.avro.ipc.stats.StatsPlugin;

value:good-to-have; category:bug; feedback: The Claude AI reviewer is correct! The ASFv2 license should be placed at the top of the source files, not in the middle or at the end of their contents. Apparently the licence checker (the RAT plugin) does not detect this, so it should be fixed too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants